package com.ds.infrastructure.hyperspace.container.flow.handler.route;

import com.ds.infrastructure.hyperspace.common.Container;
import com.ds.infrastructure.hyperspace.common.flow.FlowHandlerPipeline;
import com.ds.infrastructure.hyperspace.container.flow.context.HyperspaceDuplexFlowContext;
import com.ds.infrastructure.hyperspace.container.flow.handler.AbstractPreFlowHandler;
import com.ds.infrastructure.hyperspace.container.internal.http2.HyperspaceHttp2FrameWrapper;
import com.ds.infrastructure.hyperspace.container.internal.operator.OperatorDefinition;
import com.ds.infrastructure.hyperspace.container.internal.util.Http2Util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static com.ds.infrastructure.hyperspace.container.internal.util.StringConstant.ASTERISK;
import static com.ds.infrastructure.hyperspace.container.internal.util.StringConstant.EMPTY_STR;

/**
 * usage eg:
 * -   handler: grpcServiceRoutePreHandler
 * handlerParams: [serviceName1|serviceName1, uri|headerName, (operatorName,operatorArgs)...]
 *
 * @author  Weishaoying
 */
@Slf4j
public class GrpcServiceRoutePreFlowHandler extends AbstractPreFlowHandler {

    private static final int INPUT_NAME_INDEX = 1;

    private static final int DEFAULT_HOST_SIZE = 5;

    private volatile boolean allServiceMatch;

    private Map<String, String> serviceMap = new ConcurrentHashMap<>(DEFAULT_HOST_SIZE);

    private volatile String inputName;

    private volatile List<OperatorDefinition> operatorDefinitionList;

    @Override
    public void handleInput(HyperspaceDuplexFlowContext duplexFlowContext, Object data) {
        if (data == duplexFlowContext.getRequestStartState() && data instanceof HyperspaceHttp2FrameWrapper) {
            if (allServiceMatch) {
                duplexFlowContext.setRouteMatch(true);
                return;
            }
            if (duplexFlowContext.getGrpcServiceRouterName() == null ||
                    serviceMap.get(duplexFlowContext.getGrpcServiceRouterName()) == null) {
                return;
            }
            if (inputName != null && extractAndComputeInput(duplexFlowContext,
                    inputName, operatorDefinitionList) == null) {
                return;
            }
            duplexFlowContext.setRouteMatch(true);
        }
    }

    @Override
    public String handlerName() {
        return "grpcServiceRoutePreHandler";
    }

    /**
     * @param container
     * @param handlerParams
     */
    @Override
    public void initHandlerParameters(Container container, FlowHandlerPipeline pipeline, String[] handlerParams) {
        String grpcServiceStr = handlerParams[0];
        if (ASTERISK.equals(grpcServiceStr)) {
            allServiceMatch = true;
            pipeline.registerPipelineHost(ASTERISK);
            return;
        }
        if (grpcServiceStr.indexOf(OR_DELIMITER) == -1) {
            String serviceRouterName = Http2Util.getGrpcServiceRouterName(grpcServiceStr);
            serviceMap.put(serviceRouterName, EMPTY_STR);
            pipeline.registerPipelineHost(serviceRouterName);
        } else {
            String[] serviceStrArray = StringUtils.delimitedListToStringArray(
                    grpcServiceStr, OR_DELIMITER);
            for (String tempOneServiceStr : serviceStrArray) {
                String serviceRouterName = Http2Util.getGrpcServiceRouterName(tempOneServiceStr);
                serviceMap.put(serviceRouterName, EMPTY_STR);
                pipeline.registerPipelineHost(serviceRouterName);
            }
        }
        if (handlerParams.length > INPUT_NAME_INDEX) {
            inputName = handlerParams[INPUT_NAME_INDEX];
            operatorDefinitionList = parseOperatorDefinition(container, handlerParams, INPUT_NAME_INDEX + 1);
        }
    }

}
